package com.naiterui.ehp.bs.cms.modules.recommend.service.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Preconditions;
import com.naiterui.common.redis.RedisUtil;
import com.naiterui.ehp.bp.bo.esign.EsignNoSecretBO;
import com.naiterui.ehp.bp.bo.esign.PreSignResultBO;
import com.naiterui.ehp.bp.constants.CommonConstant;
import com.naiterui.ehp.bp.security.utils.SecurityUtils;
import com.naiterui.ehp.bp.support.exception.BaseExceptionCodes;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.utils.encypt.AESUtil;
import com.naiterui.ehp.bs.cms.common.constants.CacheConstants;
import com.naiterui.ehp.bs.cms.common.vo.PageParamsVO;
import com.naiterui.ehp.bs.cms.common.vo.PageVO;
import com.naiterui.ehp.bs.cms.modules.recommend.entity.MedicationRecom;
import com.naiterui.ehp.bs.cms.modules.recommend.entity.MedicationRecomDetail;
import com.naiterui.ehp.bs.cms.modules.recommend.entity.RecomAuditLog;
import com.naiterui.ehp.bs.cms.modules.recommend.entity.RecomAuditOperationLog;
import com.naiterui.ehp.bs.cms.modules.recommend.entity.RecommPrescription;
import com.naiterui.ehp.bs.cms.modules.recommend.feign.IEsignFeignClient;
import com.naiterui.ehp.bs.cms.modules.recommend.feign.IRecomFeignClient;
import com.naiterui.ehp.bs.cms.modules.recommend.mapper.MedicationRecomDetailMapper;
import com.naiterui.ehp.bs.cms.modules.recommend.mapper.MedicationRecomMapper;
import com.naiterui.ehp.bs.cms.modules.recommend.mapper.RecomAuditLogMapper;
import com.naiterui.ehp.bs.cms.modules.recommend.mapper.RecomAuditOperationLogMapper;
import com.naiterui.ehp.bs.cms.modules.recommend.mapper.RecommPrescriptionMapper;
import com.naiterui.ehp.bs.cms.modules.recommend.service.IMedicationRecomService;
import com.naiterui.ehp.bs.cms.modules.recommend.vo.AuditInfoVO;
import com.naiterui.ehp.bs.cms.modules.recommend.vo.MedicationRecomDetailVO;
import com.naiterui.ehp.bs.cms.modules.recommend.vo.MedicationRecomVO;
import com.naiterui.ehp.bs.cms.modules.recommend.vo.QrcodeVO;
import com.naiterui.ehp.bs.cms.modules.recommend.vo.RecomAuditOperationLogVO;
import com.naiterui.ehp.bs.cms.modules.recommend.vo.RecomFile;
import com.naiterui.ehp.bs.cms.modules.recommend.vo.RecomSku;
import com.naiterui.ehp.bs.cms.modules.recommend.vo.VerifyIdentityVO;
import com.naiterui.ehp.bs.cms.modules.user.entity.Pharmacist;
import com.naiterui.ehp.bs.cms.modules.user.mapper.PharmacistMapper;
import com.naiterui.ehp.bs.cms.modules.user.vo.SelectPharmacistVO;
import com.naiterui.ehp.bs.cms.modules.wms.entity.Sku;
import com.naiterui.ehp.bs.cms.modules.wms.mapper.SkuMapper;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * @author guoyongxiang
 */
@Slf4j
@Service
public class MedicationRecomServiceImpl extends ServiceImpl<MedicationRecomMapper, MedicationRecom>
    implements IMedicationRecomService {

    /**
     * 最大级别的药师初级职称（药士、药师）
     */
    private static final Integer JUNIOR_TITLE = 2;

    private static final String IDENTITY_REDISKEY = CacheConstants.REDIS_PREFIX + "medicationrecom_identity_";
    @Value("${ehp.domain.img}")
    private String imgDomain;
    @Autowired
    private PharmacistMapper pharmacistMapper;
    @Autowired
    private RecomAuditLogMapper recomAuditLogMapper;
    @Autowired
    private RecommPrescriptionMapper recommPrescriptionMapper;
    @Autowired
    private MedicationRecomDetailMapper medicationRecomDetailMapper;

    @Autowired
    private SkuMapper skuMapper;
    @Autowired
    private IRecomFeignClient recomFeignClient;
    @Autowired
    private RecomAuditOperationLogMapper recomAuditOperationLogMapper;
    @Autowired
    private IEsignFeignClient esignFeignClient;

    @Override
    public PageVO<RecomAuditOperationLogVO> auditLogs(PageParamsVO pageParamsVO, Long recomId) {
        QueryWrapper<RecomAuditOperationLog> entityWrapper = new QueryWrapper<>();
        entityWrapper.eq("recom_id", recomId);
        entityWrapper.orderByAsc("id");
        IPage<RecomAuditOperationLog> page =
            this.recomAuditOperationLogMapper.selectPage(pageParamsVO.getPage(), entityWrapper);
        List<RecomAuditOperationLog> records = page.getRecords();
        if (CollUtil.isEmpty(records)) {
            return PageVO.toPageVo(new Page<>());
        }
        return PageVO
            .toPageVo(new Page<RecomAuditOperationLogVO>().setTotal(page.getTotal()).setCurrent(page.getCurrent())
                .setPages(page.getPages()).setSize(page.getSize())
                .setRecords(records
                    .stream().map(r -> RecomAuditOperationLogVO.builder().remark(r.getRemark())
                        .createdBy(r.getCreatedBy()).createdAt(r.getCreatedAt()).build())
                    .collect(Collectors.toList())));
    }

    @Override
    public VerifyIdentityVO verifyIdentity(Long userId) {
        VerifyIdentityVO identityVO = new VerifyIdentityVO();
        Long pharmacistId = getBindPharmacistId(userId);
        if (pharmacistId == null) {
            // identityVO.setStatus(VerifyIdentityVO.STATUS_NEED);
            identityVO.setList(pharmacistMapper.getAllSelect());
        } else {
            identityVO.setStatus(VerifyIdentityVO.STATUS_SUCCEED);

            // 设置审核级别
            Pharmacist pharmacist = pharmacistMapper.selectById(pharmacistId);
            if (pharmacist.getTitleId() > JUNIOR_TITLE) {
                identityVO.setAuditLevel(VerifyIdentityVO.AUDIT_LEVEL_FINAL);
            } else {
                identityVO.setAuditLevel(VerifyIdentityVO.AUDIT_LEVEL_PRE);
            }
        }
        return identityVO;
    }

    @Override
    public void setIdentity(Long userId, Long pharmacistId, String idCard) throws BusinessException {
        Pharmacist pharmacist = pharmacistMapper.selectById(pharmacistId);
        if (!pharmacist.getCardNo().equalsIgnoreCase(idCard)) {
            throw new BusinessException(BaseExceptionCodes.FAILED, "身份证号不正确，请检查后重新输入！");
        }
        // 校验是否已实名，未实名提示进行实名
        SelectPharmacistVO pharmacistVO = pharmacistMapper.getPharmacist(pharmacistId);
        if (!SelectPharmacistVO.AUTH_STATUS_PASS.equals(pharmacistVO.getAuthStatus())) {
            throw new BusinessException(BaseExceptionCodes.FAILED, "药师尚未进行实名，请先进行实名认证！");
        }

        // QueryWrapper<CmsRecordAgency> entityWrapper = new QueryWrapper<>();
        // entityWrapper.orderByAsc("id");
        // List<CmsRecordAgency> cmsRecordAgencies = this.cmsRecordAgencyMapper.selectList(entityWrapper);

        RedisUtil.valueOps().setObject(IDENTITY_REDISKEY + userId, pharmacistId);

    }

    @Override
    public PreSignResultBO preSign(Long userId, Long recomId) throws BusinessException {
        Long pharmacistId = getBindPharmacistId(userId);
        if (pharmacistId == null) {
            throw new BusinessException(BaseExceptionCodes.FAILED, "当前身份校验错误，请设置药师身份！");
        }
        return recomFeignClient.preSign(recomId, pharmacistId);
    }

    @Override
    public void audit(Long userId, Long recomId, Integer status, String remark, Integer force, String signPwd,
        String uniqueId) throws BusinessException {
        if (status == 1 && (force != null && force == 0)) {
            Preconditions.checkArgument(!StringUtils.isEmpty(signPwd), "密码不能为空！");
        }
        Long pharmacistId = getBindPharmacistId(userId);
        if (pharmacistId == null) {
            throw new BusinessException(BaseExceptionCodes.FAILED, "当前身份校验错误，请设置药师身份！");
        }
        // 审核通过先签名，然后审核
        if (RecomAuditLog.AUDIT_STATUS_PASS.equals(status)) {
            if (StrUtil.isBlank(signPwd)) {
                // 开始走免密流程
                EsignNoSecretBO noSecretBO = esignFeignClient.getNoSecretInfo(pharmacistId);
                if (!noSecretBO.getNoSecret()) {
                    log.warn("医生未开通免密 doctorId:{}", pharmacistId);
                    throw new BusinessException(BaseExceptionCodes.DOCTOR_NOT_NO_SECRET);
                }
                signPwd = AESUtil.decrypt(CommonConstant.ESIGN_NO_SECRET_KEY, noSecretBO.getPin());
            }
            recomFeignClient.sign(recomId, pharmacistId, signPwd, uniqueId);
        }
        recomFeignClient.audit(recomId, status, remark, (Long)pharmacistId, force, signPwd);
    }

    @Override
    public void realnameCode(String phone) throws BusinessException {
        SelectPharmacistVO pharmacist = pharmacistMapper.getByPhone(phone);
        if (pharmacist == null) {
            throw new BusinessException(BaseExceptionCodes.FAILED, "药师不存在！");
        }
        esignFeignClient.realnameCode(pharmacist.getName(), pharmacist.getPhone(), pharmacist.getCardNo());
    }

    @Override
    public SelectPharmacistVO getPharmacistByPhone(String phone, String verifyCode) throws BusinessException {
        if (StringUtils.isEmpty(verifyCode)) {
            throw new BusinessException(BaseExceptionCodes.FAILED, "药师实名验证码不能为空！");
        }
        // 验证短信验证码，进行实名认证
        Boolean pass = esignFeignClient.realnameCheck(phone, verifyCode);
        if (!Boolean.TRUE.equals(pass)) {
            throw new BusinessException(BaseExceptionCodes.FAILED, "药师实名校验不通过，请重新核对验证码！");
        }
        return pharmacistMapper.getByPhone(phone);
    }

    @Override
    public PageVO<MedicationRecomVO> recomList(PageParamsVO pageParamsVO, MedicationRecomVO medicationRecom,
        boolean verifyPh) throws BusinessException {
        // 查询当前药师审核权限
        // this.bindVerify();
        if (verifyPh) {
            Long pharmacistId = getBindPharmacistId(SecurityUtils.getCurrentUser().getId());
            if (pharmacistId == null) {
                throw new BusinessException(BaseExceptionCodes.FAILED, "当前身份校验错误，请设置药师身份！");
            }
            medicationRecom.setPharmacistId(pharmacistId);
        }
        QueryWrapper<MedicationRecomVO> entityWrapper = this.buildEntityWrapper(pageParamsVO, medicationRecom);
        Page<MedicationRecomVO> page = this.recomAuditLogMapper.getPage(pageParamsVO.getPage(), entityWrapper);
        return PageVO.toPageVo(page);
    }

    @Override
    public MedicationRecomDetailVO recomDetail(Long recomId, boolean verifyPh) throws BusinessException {
        MedicationRecomDetailVO detailVO = recomAuditLogMapper.getDetail(recomId);
        if (verifyPh) {
            Long pharmacistId = getBindPharmacistId(SecurityUtils.getCurrentUser().getId());
            if (pharmacistId != null) {
                Pharmacist pharmacist = this.pharmacistMapper.selectById((Long)pharmacistId);
                detailVO.setPharmacistName(pharmacist.getName());
            } else {
                throw new BusinessException(BaseExceptionCodes.FAILED, "当前身份校验错误，请设置药师身份！");
            }
            detailVO.setPharmacistId(pharmacistId);
        }
        if (detailVO.getExpireAt().getTime() < System.currentTimeMillis()) {
            detailVO.setInvalid(1);
        }

        // 查询审核信息
        RecomAuditLog auditLog = recomAuditLogMapper.selectById(detailVO.getId());

        // 查询药品列表
        Map<String, Object> recomDetailParam = new HashMap<>();
        recomDetailParam.put("recommend_id", recomId);
        List<MedicationRecomDetail> skuDetails = medicationRecomDetailMapper.selectByMap(recomDetailParam);
        List<Long> skuIds = skuDetails.stream().map(MedicationRecomDetail::getSkuId).collect(Collectors.toList());
        List<Sku> skuList = skuMapper.selectBatchIds(skuIds);
        Map<Long, Sku> skuMap = skuList.stream().collect(Collectors.toMap(Sku::getId, s -> s));

        List<RecomSku> skus = skuDetails.stream()
            .map(s -> RecomSku.builder().skuId(s.getSkuId())
                .skuNumber(skuMap.get(s.getSkuId()) == null ? "" : skuMap.get(s.getSkuId()).getNumber())
                .name(s.getName()).quantity(s.getQuantity()).quantityUnit(s.getQuantityUnit())
                .usages(s.getUsages()).remark(s.getBakup()).build())
            .collect(Collectors.toList());
        detailVO.setSkus(skus);

        // 构建审核对象
        AuditInfoVO auditInfoVO = new AuditInfoVO();
        auditInfoVO.setPreStatus(auditLog.getPreAuditStatus());
        if (!RecomAuditLog.AUDIT_STATUS_PENDING.equals(auditLog.getPreAuditStatus())) {
            auditInfoVO.setPreAuditTime(auditLog.getPreAuditTime());
            auditInfoVO.setPreAuditUser(auditLog.getPrePharmacistName());
        }

        auditInfoVO.setStatus(auditLog.getStatus());
        if (!RecomAuditLog.AUDIT_STATUS_PENDING.equals(auditLog.getStatus())) {
            auditInfoVO.setAuditType(auditLog.getAuditType());
            auditInfoVO.setAuditUser(auditLog.getPharmacistName());
            auditInfoVO.setAuditTime(auditLog.getChangedAt());
            auditInfoVO.setRemark(auditLog.getRemark());
        }
        detailVO.setAudit(auditInfoVO);

        // 文件列表
        Map<String, Object> recomFileParam = new HashMap<>();
        recomFileParam.put("recommend_id", recomId);
        List<RecommPrescription> files = this.recommPrescriptionMapper.selectByMap(recomFileParam);
        List<RecomFile> recomFiles = files.stream().map(f -> {
            switch (f.getType()) {
                case RecommPrescription.TYPE_UNSIGN:
                    return RecomFile.builder().name("(初始)处方单预览").url(this.imgDomain + f.getPrescriptionPhotoUrl())
                        .build();

                case RecommPrescription.TYPE_DOCTOR_SIGN:
                    return RecomFile.builder().name("(医师已签名)处方单预览").url(this.imgDomain + f.getPrescriptionPhotoUrl())
                        .build();

                case RecommPrescription.TYPE_PHARMACIST_SIGN:
                    return RecomFile.builder().name("(药师已签名)处方单预览").url(this.imgDomain + f.getPrescriptionPhotoUrl())
                        .build();

                case RecommPrescription.TYPE_AL_PHARMACIST_SIGN:
                    return RecomFile.builder().name("(调配药师已签名)处方单预览").url(this.imgDomain + f.getPrescriptionPhotoUrl())
                        .build();

                default:
                    return RecomFile.builder().name("处方单预览").url(this.imgDomain + f.getPrescriptionPhotoUrl()).build();
            }
        }).collect(Collectors.toList());
        detailVO.setFiles(recomFiles);

        // Long pharmacistId = this.getBindPharmacistId(SecurityUtils.getCurrentUser().getId());

        return detailVO;
    }

    @Override
    public void setIdentityUncheck(Long userId, String phone, String idCard) throws BusinessException {
        SelectPharmacistVO pharmacistVO = pharmacistMapper.getByPhone(phone);
        if (!pharmacistVO.getCardNo().equalsIgnoreCase(idCard)) {
            throw new BusinessException(BaseExceptionCodes.FAILED, "身份证号不正确，请检查后重新输入！");
        }
        // 校验是否已实名，未实名提示进行实名
        if (!SelectPharmacistVO.AUTH_STATUS_PASS.equals(pharmacistVO.getAuthStatus())) {
            throw new BusinessException(BaseExceptionCodes.FAILED, "药师尚未进行实名，请先进行实名认证！");
        }
        RedisUtil.valueOps().setObject(CommonConstant.IDENTITY_REDISKEY + userId, pharmacistVO.getId());
    }

    @Override
    public QrcodeVO qrcode(Long userId, String uuid) throws BusinessException {
        Integer status = QrcodeVO.STATUS_UNBOUND;
        String value = null;
        Long pharmacistId = getBindPharmacistId(userId);
        log.info("获取绑定药师二维码 pharmacistId={}", pharmacistId);
        if (pharmacistId != null) {
            Pharmacist pharmacist = pharmacistMapper.selectById(pharmacistId);
            if (pharmacist == null) {
                throw new BusinessException(BaseExceptionCodes.FAILED, "药师信息获取失败，请稍后再试");
            }
            status = QrcodeVO.STATUS_BOUND;
            value = pharmacist.getName();
        } else if (!StringUtils.isEmpty(uuid)) {
            String val = RedisUtil.valueOps().getString(CommonConstant.IDENTITY_KEY_REDISKEY + uuid);
            value = "cmsId=" + userId + "&uuid=" + uuid;
            // 如果缓存信息为空，代表二维码已过期
            if (StringUtils.isEmpty(val)) {
                status = QrcodeVO.STATUS_FAIL;
                value = null;
            }
        } else {
            uuid = UUID.randomUUID().toString();
            RedisUtil.valueOps().set(CommonConstant.IDENTITY_KEY_REDISKEY + uuid, userId,
                CommonConstant.IDENTITY_KEY_EXPIRED_REDISKEY);
            value = "cmsId=" + userId + "&uuid=" + uuid;
        }
        return QrcodeVO.builder().status(status).value(value).uuid(uuid).build();
    }

    @Override
    public void untie(Long userId) throws BusinessException {
        Long pharmacistId = getBindPharmacistId(userId);
        if (pharmacistId == null) {
            throw new BusinessException(BaseExceptionCodes.PARAM_ERROR, "未绑定药师！");
        }
        RedisUtil.keyOps().delete(CommonConstant.IDENTITY_REDISKEY + userId);
        RedisUtil.keyOps().delete(CommonConstant.IDENTITY_PH_REDISKEY + pharmacistId);
    }

    private QueryWrapper<MedicationRecomVO> buildEntityWrapper(PageParamsVO pageParamsVO,
        MedicationRecomVO medicationRecomVO) {

        QueryWrapper<MedicationRecomVO> entityWrapper = pageParamsVO.getEntityWrapper("a");
        entityWrapper.like(!StringUtils.isEmpty(medicationRecomVO.getDoctorName()), "a.doctor_name",
            medicationRecomVO.getDoctorName());
        entityWrapper.like(!StringUtils.isEmpty(medicationRecomVO.getPatientName()), "a.patient_name",
            medicationRecomVO.getPatientName());
        entityWrapper.eq(medicationRecomVO.getStatus() != null, "a.status", medicationRecomVO.getStatus());
        entityWrapper.eq(!StringUtils.isEmpty(medicationRecomVO.getSerialNumber()), "a.serial_number",
            medicationRecomVO.getSerialNumber());
        entityWrapper.like(!StringUtils.isEmpty(medicationRecomVO.getDoctorPhone()), "d.phone",
            medicationRecomVO.getDoctorPhone());
        entityWrapper.like(!StringUtils.isEmpty(medicationRecomVO.getPatientPhone()), "p.phone",
            medicationRecomVO.getPatientName());
        entityWrapper.eq(medicationRecomVO.getPharmacistId() != null, "a.pharmacist_id",
            medicationRecomVO.getPharmacistId());

        return entityWrapper;
    }

    private Long getBindPharmacistId(Long userId) {
        return (Long)RedisUtil.valueOps().getObject(CommonConstant.IDENTITY_REDISKEY + userId);
    }

    private Pharmacist bindVerify() throws BusinessException {
        Long pharmacistId = getBindPharmacistId(SecurityUtils.getCurrentUser().getId());
        if (pharmacistId == null) {
            throw new BusinessException(BaseExceptionCodes.FAILED, "请先绑定药师！");
        }

        Pharmacist pharmacist = pharmacistMapper.selectById(pharmacistId);
        if (pharmacist == null) {
            throw new BusinessException(BaseExceptionCodes.FAILED, "药师ID不存在！pharmacistId：" + pharmacistId);
        }
        return pharmacist;
    }
}
